<!--
  @author: cqz
  @desc: 分类树维护通用组件 包含拖拽 上移  下移 带isCat字段
  @params: 获取列表地址格式约定： {moduleUrl}/list  新增地址 {moduleUrl}/save 修改地址 {moduleUrl}/update 单个信息查询 {moduleUrl}/info
  @date: 2020-12-22
-->
<template>
  <div ref="dirTreeContainer">
    <el-row type="flex" style="height: 40px; margin-bottom: 5px; line-height: 40px;">
      <el-input v-model="filterText"
                prefix-icon="el-icon-search"
                placeholder="输入过滤关键字"
                clearable style="flex: 1;"
                maxlength="30"></el-input>
      <div style="margin-left: 5px;" v-if="isAuth(moduleAuth + ':save') && !readonly">
        <el-button
          size="small"
          icon="el-icon-folder-add"
          circle title="添加分类"
          :disabled="isCat !== $YES"
          @click="addOrUpdateHandle('1')">
        </el-button>
        <el-button v-if="addBss"
          size="small"
          icon="el-icon-plus" :disabled="isCat !== $YES"
          :title="'新增' + this.bssTitle"
          circle style="margin-left: 0px;"
          type="success"
          @click="addOrUpdateHandle('0')"></el-button>
      </div>
    </el-row>
    <el-scrollbar :style="{ height: (mHeight !== null ? mHeight : domHeight) + 'px'}">
      <el-tree :data="dataList"
               :auto-expand-parent="false"
               v-loading="loading"
               :props="listProps" draggable
               @node-drop="dragEndHandler"
               @node-contextmenu="onRightClick"
               :allow-drop="propsAllowDrop == null ? allowDrop : propsAllowDrop"
               :key="keyRefresh"
               @current-change="query"
               :default-expanded-keys="expandedList"
               :expand-on-click-node="true"
               :highlight-current="true"
               :filter-node-method="filterNode"
               @node-expand="nodeExpand"
               @node-collapse="nodeCollapse"
               ref="tree"
               node-key="id">
        <div class="custom-tree-node"
             slot-scope="{ node, data }"
             @mouseenter="mouseEnter(data)"
             @mouseleave="mouseLeave(data)">
                <span class="node-icon">
                  <span v-if="data.icon">
                    <icon-svg :name="data.icon"></icon-svg>
                  </span>
                  <span v-else-if="data.isCat===$YES">
                    <svg-icon icon-class="icon-opened" v-show="node.expanded"></svg-icon>
                    <svg-icon icon-class="icon-closed" v-show="!node.expanded"></svg-icon>
                  </span>
                  <span v-else>
                    <svg-icon icon-class="icon-table"></svg-icon>
                  </span>
                </span>
          <span>{{ node.label }}</span>
          <span class="tree_node_op" v-if="node.level > 1 && data.show && !readonly">
                  <i class="el-icon-top" title="上移"
                     v-on:click.stop="() => moveHandle(node, data, true)"></i>
                  <i class="el-icon-bottom" title="下移"
                     v-on:click.stop="() => moveHandle(node, data, false)"></i>
                  <i class="el-icon-edit"
                     v-if="isAuth(moduleAuth + ':update')" title="修改"
                     @click="() => addOrUpdateHandle(data.isCat, data.id)"></i>
                  <i class="el-icon-delete"
                     v-if="isAuth(moduleAuth + ':delete') && (!data.children || data.children.length === 0)" title="删除"
                     v-on:click.stop="() => deleteHandle(node, data)"></i>
                </span>
        </div>
      </el-tree>
    </el-scrollbar>
    <!-- 弹窗, 新增 / 修改 -->
    <add-or-update v-if="addOrUpdateVisible" :moduleUrl="moduleUrl" :keyLabel="keyLabel"
                        ref="addOrUpdate"
                        @refreshDataList="addOrUpdateBack"></add-or-update>
    <!-- 右键菜单 -->
    <el-popover
      ref="rightMenu"
      placement="right"
      class="rightMenu"
      style="position: absolute"
      :transition="null"
      v-model="rightMenuVisible">
      <div @click="copy">复制</div>
      <div @click="paste" :class="{rightMenuItemDisabled: !copyItem}">粘贴</div>
    </el-popover>
  </div>
</template>

<script>
  import {treeDataTranslate} from '@/utils'
  // import
  import addOrUpdate from './dir-add-or-update'

  export default {
    components: {
      addOrUpdate
    },
    props: {
      moduleAuth: {
        type: String
      },
      keyLabel: {
        default: 'name',
        type: String
      },
      /***
       * 模块URL 如: std/meta/type/
       */
      moduleUrl: {
        default: '',
        type: String
      },
      /****
       * 是否添加业务项 分类之外
       */
      addBss: {
        default: false,
        type: Boolean
      },
      bssTitle: {
        default: ''
      },
      /***
       * 拖拽判断
       */
      propsAllowDrop: {
        type: Function,
        default: null
      },
      /**
       * 拖拽结束事件
       */
      propsDropEnd: {
        type: Function,
        default: null
      },
      mHeight: {
        type: Number,
        default: null
      },
      needCopy: {
        type: Boolean,
        default: false
      },
      /**
       * 只读，禁止修改
       */
      readonly: {
        type: Boolean,
        default: false
      }
    },
    watch: {
      filterText (val) {
        this.$refs.tree.filter(val)
      },
      rightMenuVisible (value) {
        if (value) {
          document.body.addEventListener('click', this.closeRightMenu, true)
        } else {
          document.body.removeEventListener('click', this.closeRightMenu)
        }
      }
    },
    data () {
      return {
        addOrUpdateVisible: false,
        loading: false,
        keyRefresh: 0,
        id: '', // 当前节点ID
        isCat: '', // 当前节点类型
        dataList: [],
        listProps: {
          label: this.keyLabel,
          children: 'children'
        },
        expandedList: [],
        filterText: '',
        rightMenuVisible: false,
        copyItem: false,
        curRightItem: ''
      }
    },
    computed: {
      domHeight: {
        get () {
          return this.$store.state.common.documentClientHeight - this.$BODY_MARGIN - 55
        }
      }
    },
    created () {
      this.init()
    },
    methods: {
      mouseEnter (data) {
        this.$set(data, 'show', true)
      },
      mouseLeave (data) {
        this.$set(data, 'show', false)
      },
      /****
       * 节点选中事件
       *@data 节点数据
       */
      query (data, node) {
        this.isCat = data.isCat
        this.id = data.id
        this.$emit('node-click', data, node)
      },
      init (triggerClick = true) {
        console.log(this.moduleUrl)
        this.copyItem = false
        this.url = '/' + this.moduleUrl + 'list'
        this.loading = true
        this.$http({
          url: this.$http.adornUrl(this.url),
          method: 'get',
          params: this.$http.adornParams({
            'isCat': this.addBss ? '' : '1'
          })
        }).then(({data}) => {
          if (data && data.code === 0) {
            this.dataList = treeDataTranslate(data.list)
            if (this.dataList && this.dataList.length > 0) {
              this.expandedList.push(this.dataList[0].id)
              this.$nextTick(() => {
                if (this.id) {
                  this.$refs.tree.setCurrentKey(this.id)
                } else {
                  this.$refs.tree.setCurrentKey(this.dataList[0].id)
                }
                var data = this.$refs.tree.getCurrentNode()
                if (triggerClick) {
                  // 判断是否触发外部点击事件
                  this.query(data)
                } else {
                  this.isCat = data.isCat
                  this.id = data.id
                }
              })
            }
          } else {
            this.dataList = []
          }
          this.loading = false
        }).catch((error) => {
          this.dirLoading = false
          console.log(error)
          this.$message.error(error.response.data.message)
        })
      },
      filterNode (value, data) {
        if (!value) return true
        return data[this.listProps.label].indexOf(value) !== -1
      },
      setCurrentKey (key) {
        this.$refs.tree.setCurrentKey(key)
      },
      addOrUpdateBack (data, copyData) {
        const newChild = JSON.parse(JSON.stringify(data))
        if (copyData) {
          // 如果是复制则选择粘贴父节点，否则选择当前选中节点
          this.currentData = copyData
        } else {
          this.currentData = this.$refs.tree.getCurrentNode()
        }
        if (data.isSave) {
          if (!this.currentData.children) {
            this.$set(this.currentData, 'children', [])
          }
          this.currentData.children.push(newChild)
        } else {
          if (newChild.orderNum !== this.currentData.orderNum) {
            // 更新排序号则刷新
            this.init(false)
            return
          }
          if (newChild) {
            for (var key in newChild) {
              if (newChild[key] != null) {
                this.currentData[key] = newChild[key]
              }
            }
          }
        }
      },
      /**
       * 新增分类
       * isCat: 类型
       */
      addOrUpdateHandle (isCat, id) {
        this.addOrUpdateVisible = true
        var obj = id ? this.$refs.tree.getNode(id).data : this.$refs.tree.getCurrentNode()
        var orderNum = 0
        if (obj) {
          orderNum = (obj.children || []).length
          if (!obj._level) {
            obj._level = this.$refs.tree.getNode(id || this.$refs.tree.getCurrentKey()).level
          }
        }
        this.$nextTick(() => {
          this.$refs.addOrUpdate.init(isCat, id, orderNum, obj, this.bssTitle)
        })
      },
      /**
       * 删除树节点
       * @param id
       * @param title
       * @param node
       * @param data
       */
      deleteHandle (node, data) {
        var that = this
        var parent = node.parent
        var children = parent.data.children || parent.data
        var index = children.findIndex(d => d.id === data.id)
        this.$confirm(
          `确定删除 ${data[this.keyLabel]} 及分组内的数据?`,
          '提示',
          {
            confirmButtonText: '确定',
            cancelButtonText: '取消',
            type: 'warning'
          }
        ).then(() => {
          this.$http({
            url: this.$http.adornUrl('/' + this.moduleUrl + 'delete'),
            method: 'post',
            data: this.$http.adornData({id: data.id, isCat: data.isCat}, false)
          }).then(({data}) => {
            if (data && data.code === 0) {
              this.$message({
                message: '操作成功',
                type: 'success',
                duration: 1500,
                onClose: () => {
                  that.$refs.tree.remove(children[index])
                  this.currentData = null
                }
              })
            } else {
              this.$message.error(data.msg)
            }
          })
        }).catch(() => {
        })
      },
      allowDrop (node, destNode, type) {
        if (destNode.level === 1) {
          return type === 'inner'
        } else {
          return true
        }
      },
      dragEndHandler (node, destNode, pos, ev) {
        if (this.propsDropEnd) {
          // 有传入方法则替代
          this.propsDropEnd(node, destNode, pos)
          return
        }
        if (destNode) {
          var parent = destNode.parent
          if (pos === 'inner') {
            node.data.parentId = destNode.data.id
            parent = destNode
          } else {
            node.data.parentId = destNode.data.parentId
          }
          var postData = []
          var start = parent.childNodes.length
          for (var i = 0; i < parent.childNodes.length; i++) {
            if (parent.childNodes[i].data.id === node.data.id) {
              start = i
              if (i > 0) {
                parent.childNodes[i].data.orderNum = parent.childNodes[i - 1].data.orderNum + 1
              } else {
                parent.childNodes[i].data.orderNum = i + 1
              }
              postData.push(parent.childNodes[i].data)
            } else if (i > start) {
              // 从被拖动节点往后更新排序号
              // parent.childNodes[i].data.orderNum = i + 1
              parent.childNodes[i].data.orderNum = parent.childNodes[i - 1].data.orderNum + 1
              postData.push(parent.childNodes[i].data)
            }
          }
          if (postData.length > 0) {
            this.$http({
              url: this.$http.adornUrl('/' + this.moduleUrl + `dragUpdate`),
              method: 'post',
              data: this.$http.adornParams(postData, false)
            }).then(({data}) => {
              if (data && data.code === 0) {
              } else {
                this.$message.error(data.msg)
              }
            })
          }
        }
      },

      moveHandle (node, data, isUp) {
        const parent = node.parent
        // 根节点，直接退出
        if (parent.level === 0) {
          return
        }
        const children = parent.data.children || parent.data
        const cIndex = children.findIndex(d => d.id === data.id)
        const cLength = children.length - 1 // 最边上的节点

        if ((isUp && cIndex === 0) || (!isUp && cIndex === cLength)) { // 上移到第一个或下移到最后一个，就路过不处理

        } else {
          let tmpIndex
          if (isUp) {
            tmpIndex = cIndex - 1
          } else {
            tmpIndex = cIndex + 1
          }
          const tempNode1 = children[tmpIndex]
          const tempNode2 = children[cIndex]

          var postData = []
          let orderNum = tempNode1.orderNum
          tempNode1.orderNum = tempNode2.orderNum
          tempNode2.orderNum = orderNum
          postData.push(tempNode1)
          postData.push(tempNode2)
          if (postData.length > 0) {
            this.$http({
              url: this.$http.adornUrl('/' + this.moduleUrl + `dragUpdate`),
              method: 'post',
              data: this.$http.adornParams(postData, false)
            }).then(({data}) => {
              if (data && data.code === 0) {
                this.$set(children, tmpIndex, tempNode2)
                this.$set(children, cIndex, tempNode1)
                this.keyRefresh++
              } else {
                this.$message.error(data.msg)
              }
            })
          }
        }
      },
      // 展开
      nodeExpand (data, node, ele) {
        console.log(data.elementName + '========expanded')
        console.log(node.expanded)
        if (this.expandedList.indexOf(data.id) < 0) {
          this.expandedList.push(data.id)
        }
        console.log(this.expandedList.length)
      },
      // 折叠
      nodeCollapse (data, node, ele) {
        console.log(data.elementName + '========nodeCollapse')
        var i = this.expandedList.indexOf(data.id)
        // for (i = 0; i < this.expandedList.length; i++) {
        //   if (this.expandedList[i] === data.id) {
        //     break
        //   }
        // }
        console.log('i============' + i)
        if (i > -1) {
          this.expandedList.splice(i, 1)
        }
        console.log('length============' + this.expandedList.length)
      },
      load (node, treeNode, resolve) {
        this.$http({
          url: this.$http.adornUrl(this.moduleUrl + `list/${node.id}`),
          method: 'get'
        }).then(({data}) => {
          if (data && data.code === 0) {
            var dictList = data.list
            for (var i = 0; i < dictList.length; i++) {
              this.$set(dictList[i], 'hasChildren', true)
            }
            resolve(dictList)
          }
        })
      },
      onRightClick (event, item) {
        if (!this.needCopy) {
          return
        }
        this.curRightItem = item
        this.$refs.rightMenu.$el.style.top = event.clientY - this.$refs.dirTreeContainer.getBoundingClientRect().top + 10 + 'px'
        this.$refs.rightMenu.$el.style.left = event.clientX - this.$refs.dirTreeContainer.getBoundingClientRect().left + 20 + 'px'
        this.rightMenuVisible = true
      },
      closeRightMenu () {
        this.rightMenuVisible = false
      },
      copy () {
        this.copyItem = this.curRightItem
      },
      paste (event) {
        if (!this.copyItem) {
          event.stopPropagation()
          return
        }
        let saveItem = JSON.parse(JSON.stringify(this.copyItem))
        saveItem.level = this.$refs.tree.getNode(this.curRightItem.id).level + 1
        saveItem.parentId = this.curRightItem.id
        saveItem.id = ''
        this.$http({
          url: this.$http.adornUrl('/' + this.moduleUrl + 'save'),
          method: 'post',
          data: this.$http.adornData(saveItem)
        }).then(({ data }) => {
          if (data && data.code === 0) {
            this.$message.success('拷贝成功')
            data.data.isSave = true
            this.addOrUpdateBack(data.data, this.curRightItem)
          } else {
            this.$message.error(data.msg ? data.msg : '拷贝失败')
          }
        })
      }
    }
  }
</script>

<style scoped>
  /deep/ .rightMenu > .el-popover {
    padding: 5px 0;
    min-width: 120px;
  }
  /deep/ .rightMenu > .el-popover > div {
    user-select: none;
    color: black;
    padding: 5px 15px;
    cursor: default;
  }
  /deep/ .rightMenu > .el-popover > div:hover {
    background-color: lightgray;
  }
  .rightMenuItemDisabled {
    color: lightgray !important;
  }
  .rightMenuItemDisabled:hover {
    background-color: white !important;
  }
</style>


